extern "C" {
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/socket.h>
}
#include <string>
#include <vector>
#include "logger.hpp"
#include "utils.hpp"
#include "connection.hpp"
#include "http_request.hpp"

namespace http {

  static const char CRLF[] = "\r\n";
  static const char CRLFCRLF[] = "\r\n\r\n";

  static void parse_request(const std::string request, string_map& parts);
  static void request_line(std::string& request, string_map& parts);
  static void headers(std::string& request, string_map& parts);
  static void message_body(std::string& request, string_map& parts);

  State recv_request(Connection& conn) {
    const int acc = conn.c_socket();
    string_map& map = conn.request(); 
    char buf[4096];
    std::string request;
    std::string::size_type content_length = 0;
    while (true) {
      int len = ::recv(acc, buf, sizeof(buf), 0);
      if (len < 0) {
        logger::log("warn", __FILE__, __LINE__, "recv error: %s", strerror(errno));
        switch (errno) {
          case EAGAIN:
            continue; // retry
          case EINTR:
            return STATE_INTR;
          default:
            return STATE_ERROR;
        }
      }
      if (len == 0) {
        logger::log("info", __FILE__, __LINE__, "connection shutdown");
        return STATE_CLOSE; // クライアントが閉じたので
      }
      if (map.empty()) {
        request.append(buf, len);             // まだ parse してないので
      } else {
        map["message-body"].append(buf, len); // parse 後なので message-body 側へ
      }
      // もう parse して良ければ，parse する
      if (request.size() >= 4) {
        std::string::size_type pos = request.rfind(CRLFCRLF);
        if (pos != std::string::npos) {
          parse_request(request, map);
          content_length = ::atoi(map["Content-Length"].c_str());
        }
      }
      // parse した後で，message-body 取り終えたらループ抜ける
      if (!map.empty() && content_length <= map["message-body"].length()) {
        break;
      }
    }
    return STATE_REQUEST_END;
  }


  static void parse_request(const std::string request, string_map& parts) {
    parts.clear();
    std::string tmp = request;  // copy ...orz
    request_line(tmp, parts);
    headers(tmp, parts);
    message_body(tmp, parts);
  }

  static void request_line(std::string& request, string_map& parts) {
    std::string::size_type pos = request.find(CRLF);
    if (pos != std::string::npos) {
      const std::string line = request.substr(0, pos);
      request.assign(request.substr(pos + strlen(CRLF)));

      std::vector<std::string> elems;
      util::split(line, " ", elems);
      parts.insert(string_map::value_type("Method", elems[0]));
      parts.insert(string_map::value_type("Request-URI", elems[1]));
      parts.insert(string_map::value_type("HTTP-Version", elems[2]));
    }
  }

  static void header_line(const std::string line, string_map& parts);

  static void headers(std::string& request, string_map& parts) {
    std::string::size_type pos = std::string::npos;
    while ((pos = request.find(CRLF)) != std::string::npos) {
      const std::string line = request.substr(0, pos);
      request.assign(request.substr(pos + strlen(CRLF)));
      if (line.empty()) {
        // headers パートの終わり
        return;
      }
      header_line(line, parts);
    }
  }

  static void header_line(const std::string line, string_map& parts) {
    static const char SEP[] = ":";
    std::string::size_type i = line.find(SEP);
    if (i != std::string::npos) {
      parts.insert(
          string_map::value_type(line.substr(0, i),
                                  util::trim(line.substr(i+strlen(SEP)))));
    }
  }

  static void message_body(std::string& request, string_map& parts) {
    parts.insert(string_map::value_type("message-body", request.substr(0)));
    request.clear();  // 全部取り終えた
  }

}

